package com.letoken.platform.repo.app.redis;

import com.letoken.platform.pub.constant.RedisKeyConstant;
import com.letoken.platform.pub.util.BeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicInteger;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 *
 * @version 1.0
 * @author： letokenlabs
 * @date： 2021/11/24 9:55
 * 黑名单钱包
 */
@Slf4j
public class AppWalletRateLimitRedis {
    private StringRedisTemplate stringRedisTemplate;

    private AppWalletRateLimitRedis() {
        stringRedisTemplate = BeanUtil.getBean(StringRedisTemplate.class);
    }

    private enum Singleton {
        INSTANCE;
        private AppWalletRateLimitRedis instance;

        // JVM会保证此方法绝对只调用一次
        Singleton() {
            instance = new AppWalletRateLimitRedis();
        }
    }

    String cacheKey = RedisKeyConstant.LETOKEN_USER_WALLET_BLACK;

    public static AppWalletRateLimitRedis me() {
        return AppWalletRateLimitRedis.Singleton.INSTANCE.instance;
    }

    /**
     * 是否触发频率限制
     * @param userId
     * @param key
     * @param limitsPerSecond 1秒内多少次
     * @return
     * @throws Exception
     */
    public boolean isLimited(String userId, String key, int limitsPerSecond) {
        return isLimited(userId, key, limitsPerSecond, 1L);
    }

    public boolean isLimited(String userId, String key, int limitCount, long durationSec) {
        String redisKey = String.format("%s:%s:%s", cacheKey, userId, key);
        try {
            RedisAtomicInteger atomicInteger = new RedisAtomicInteger(redisKey, stringRedisTemplate.getConnectionFactory());
            int incrResult = atomicInteger.incrementAndGet();
            if (incrResult == 1) {
                stringRedisTemplate.expire(redisKey, durationSec, TimeUnit.SECONDS);
            }
            //防止出现并发操作未设置超时时间的场景,这样key就是永不过期,存在风险
            if (stringRedisTemplate.getExpire(redisKey, TimeUnit.SECONDS) == -1) {
                //设置永不过期的时间
                stringRedisTemplate.expire(redisKey, durationSec, TimeUnit.SECONDS);
            }
            if (incrResult > limitCount) {
                return true;
            }
        } catch (Exception e) {
            //出现故障时，删除key
            log.error("isLimited error!" + redisKey + e.getMessage(), e);
            stringRedisTemplate.expire(redisKey, durationSec, TimeUnit.MILLISECONDS);
        }
        return false;
    }
}
